/*
 * Project: Digital Wristwatch
 * Author: Zak Kemble, contact@zakkemble.co.uk
 * Copyright: (C) 2013 by Zak Kemble
 * License: GNU GPL v3 (see License.txt)
 * Web: http://blog.zakkemble.co.uk/diy-digital-wristwatch/
 */

#include <avr/pgmspace.h>
#include <string.h>
#include "common.h"


//extern const byte smallFont[][5];
// 5x8
const byte smallFont[][5] PROGMEM ={
{0x00,0x00,0x00,0x00,0x00}, // space
{0x00,0x5F,0x00,0x00,0x00}, // !
{0x00,0x07,0x00,0x07,0x00}, // "
{0x14,0x7F,0x14,0x7F,0x14}, // #
{0x24,0x2A,0x7F,0x2A,0x12}, // $
{0x23,0x13,0x08,0x64,0x62}, // %
{0x36,0x49,0x55,0x22,0x50}, // &
{0x00,0x05,0x03,0x00,0x00}, // '
{0x1C,0x22,0x41,0x00,0x00}, // (
{0x41,0x22,0x1C,0x00,0x00}, // )
{0x08,0x2A,0x1C,0x2A,0x08}, // *
{0x08,0x08,0x3E,0x08,0x08}, // +
{0xA0,0x60,0x00,0x00,0x00}, // ,
{0x08,0x08,0x08,0x08,0x08},
{0x60,0x60,0x00,0x00,0x00},
{0x20,0x10,0x08,0x04,0x02},
{0x3E,0x51,0x49,0x45,0x3E},
{0x00,0x42,0x7F,0x40,0x00},
{0x62,0x51,0x49,0x49,0x46},
{0x22,0x41,0x49,0x49,0x36},
{0x18,0x14,0x12,0x7F,0x10},
{0x27,0x45,0x45,0x45,0x39},
{0x3C,0x4A,0x49,0x49,0x30},
{0x01,0x71,0x09,0x05,0x03},
{0x36,0x49,0x49,0x49,0x36},
{0x06,0x49,0x49,0x29,0x1E},
{0x00,0x36,0x36,0x00,0x00},
{0x00,0xAC,0x6C,0x00,0x00},
{0x08,0x14,0x22,0x41,0x00},
{0x14,0x14,0x14,0x14,0x14},
{0x41,0x22,0x14,0x08,0x00},
{0x02,0x01,0x51,0x09,0x06},
{0x32,0x49,0x79,0x41,0x3E},
{0x7E,0x09,0x09,0x09,0x7E},
{0x7F,0x49,0x49,0x49,0x36},
{0x3E,0x41,0x41,0x41,0x22},
{0x7F,0x41,0x41,0x22,0x1C},
{0x7F,0x49,0x49,0x49,0x41},
{0x7F,0x09,0x09,0x09,0x01},
{0x3E,0x41,0x41,0x51,0x72},
{0x7F,0x08,0x08,0x08,0x7F},
{0x41,0x7F,0x41,0x00,0x00},
{0x20,0x40,0x41,0x3F,0x01},
{0x7F,0x08,0x14,0x22,0x41},
{0x7F,0x40,0x40,0x40,0x40},
{0x7F,0x02,0x0C,0x02,0x7F},
{0x7F,0x04,0x08,0x10,0x7F},
{0x3E,0x41,0x41,0x41,0x3E},
{0x7F,0x09,0x09,0x09,0x06},
{0x3E,0x41,0x51,0x21,0x5E},
{0x7F,0x09,0x19,0x29,0x46},
{0x26,0x49,0x49,0x49,0x32},
{0x01,0x01,0x7F,0x01,0x01},
{0x3F,0x40,0x40,0x40,0x3F},
{0x1F,0x20,0x40,0x20,0x1F},
{0x3F,0x40,0x38,0x40,0x3F},
{0x63,0x14,0x08,0x14,0x63},
{0x03,0x04,0x78,0x04,0x03},
{0x61,0x51,0x49,0x45,0x43},
{0x7F,0x41,0x41,0x00,0x00},
{0x02,0x04,0x08,0x10,0x20},
{0x41,0x41,0x7F,0x00,0x00},
{0x04,0x02,0x01,0x02,0x04},
{0x80,0x80,0x80,0x80,0x80},
{0x01,0x02,0x04,0x00,0x00},
{0x20,0x54,0x54,0x54,0x78},
{0x7F,0x48,0x44,0x44,0x38},
{0x38,0x44,0x44,0x28,0x00},
{0x38,0x44,0x44,0x48,0x7F},
{0x38,0x54,0x54,0x54,0x18},
{0x08,0x7E,0x09,0x02,0x00},
{0x18,0xA4,0xA4,0xA4,0x7C},
{0x7F,0x08,0x04,0x04,0x78},
{0x00,0x7D,0x00,0x00,0x00},
{0x80,0x84,0x7D,0x00,0x00},
{0x7F,0x10,0x28,0x44,0x00},
{0x41,0x7F,0x40,0x00,0x00},
{0x7C,0x04,0x18,0x04,0x78},
{0x7C,0x08,0x04,0x7C,0x00},
{0x38,0x44,0x44,0x38,0x00},
{0xFC,0x24,0x24,0x18,0x00},
{0x18,0x24,0x24,0xFC,0x00},
{0x00,0x7C,0x08,0x04,0x00},
{0x48,0x54,0x54,0x24,0x00},
{0x04,0x7F,0x44,0x00,0x00},
{0x3C,0x40,0x40,0x7C,0x00},
{0x1C,0x20,0x40,0x20,0x1C},
{0x3C,0x40,0x30,0x40,0x3C},
{0x44,0x28,0x10,0x28,0x44},
{0x1C,0xA0,0xA0,0x7C,0x00},
{0x44,0x64,0x54,0x4C,0x44},
{0x08,0x36,0x41,0x00,0x00},
{0x00,0x7F,0x00,0x00,0x00}, // {
{0x41,0x36,0x08,0x00,0x00}, // |
{0x02,0x01,0x01,0x02,0x01}, // }
{0x02,0x05,0x05,0x02,0x00} // ~
};

inline static void setBuffByte(byte*, byte, byte, byte);//, byte);

void draw_string(char* string, bool invert, byte x, byte y)
{
	s_image img = newImage(0, y, NULL, SMALLFONT_WIDTH, SMALLFONT_HEIGHT, WHITE, invert, 0);

	byte charCount = 0;
	while(*string)
	{
		char c = *string - 0x20;
		byte xx = x + (charCount*6);
		img.x = xx;
		img.bitmap = smallFont[(byte)c];
		draw_bitmap_s2(&img);
		if(invert)
		{
			if(xx > 0)
				setBuffByte(oledBuffer, xx-1, y, 0xFF);//, WHITE);
			if(xx < FRAME_WIDTH - 5)
				setBuffByte(oledBuffer, xx+5, y, 0xFF);//, WHITE);
		}
		string++;
		charCount++;
	}
}
/*
// Special draw string
// Doesn't draw spaces
// Colons and spaces have smaller width
void draw_string_time(char* string, bool invert, byte x, byte y)
{
	byte charCount = 0;
	while(*string)
	{
		char c = *string;
		if(c != ' ')
		{
			byte b = c;
			c -= 0x30;
			if(b != ':')
				draw_bitmap(x + (charCount), y, midFont[(byte)c], 19, 24, WHITE, invert, 0);
			else
			{
				draw_bitmap(x + (charCount), y, colon, 6, 24, WHITE, invert, 0);
				charCount-=10;
			}
			charCount+=21;
		}
		else
			charCount+=6;
		string++;
	}
}
*/

inline static void setBuffByte(byte* buff, byte x, byte y, byte val)//, byte colour)
{
	uint pos = x + (y / 8) * FRAME_WIDTH;
	buff[pos] |= val;
}

inline static byte readPixels(const byte* loc, bool invert)
{
	byte pixels = pgm_read_byte(loc);
	if(invert)
		pixels = ~pixels;
	return pixels;
}

// Ultra fast bitmap drawing
// Only downside is that height must be a multiple of 8, otherwise it gets rounded down to the nearest multiple of 8
// Drawing bitmaps that are completely on-screen and have a Y co-ordinate that is a multiple of 8 results in best performance
// PS - Sorry about the poorly named variables ;_;
// Optimize: Use a local variable temp buffer then apply to global variable OLED buffer?
void draw_bitmap_s2(s_image* image)
{
	byte x = image->x;
	byte yy = image->y;
	const byte* bitmap = image->bitmap;
	byte w = image->width;
	byte h = image->height;
//	byte colour = image->foreColour;
	bool invert = image->invert;
	byte offsetY = image->offsetY;

	// 
	byte y = yy - offsetY;

	// 
	byte h2 = h / 8;
	
	// 
	byte pixelOffset = (y % 8);

	byte thing3 = (yy+h);

	// 
	for(byte hh=0;hh<h2;hh++)
	{
		// 
		byte hhh = (hh*8) + y;
		byte hhhh = hhh + 8;

		// 
		if(offsetY && (hhhh < yy || hhhh > FRAME_HEIGHT || hhh > thing3))
			continue;

		// 
		byte offsetMask = 0xFF;
		if(offsetY)
		{
			if(hhh < yy)
				offsetMask = (0xFF<<(yy-hhh));
			else if(hhhh > thing3)
				offsetMask = (0xFF>>(hhhh-thing3));
		}

		uint aa = ((hhh / 8) * FRAME_WIDTH);

		// If() outside of loop makes it faster (doesn't have to kee re-evaluating it)
		// Downside is code duplication
		if(!pixelOffset && hhh < FRAME_HEIGHT)
		{
			// 
			for(byte ww=0;ww<w;ww++)
			{
				// Workout X co-ordinate in frame buffer to place next 8 pixels
				byte xx = ww + x;

				// Stop if X co-ordinate is outside the frame
				if(xx >= FRAME_WIDTH)
					continue;

				// Read pixels
				byte pixels = readPixels((bitmap + (hh*w)) + ww, invert) & offsetMask;

				oledBuffer[xx + aa] |= pixels;

				//setBuffByte(buff, xx, hhh, pixels, colour);
			}
		}
		else
		{
			uint aaa = ((hhhh / 8) * FRAME_WIDTH);
			
			// 
			for(byte ww=0;ww<w;ww++)
			{
				// Workout X co-ordinate in frame buffer to place next 8 pixels
				byte xx = ww + x;

				// Stop if X co-ordinate is outside the frame
				if(xx >= FRAME_WIDTH)
					continue;

				// Read pixels
				byte pixels = readPixels((bitmap + (hh*w)) + ww, invert) & offsetMask;

				// 
				if(hhh < FRAME_HEIGHT)
					oledBuffer[xx + aa] |= pixels << pixelOffset;
					//setBuffByte(buff, xx, hhh, pixels << pixelOffset, colour);				

				// 
				if(hhhh < FRAME_HEIGHT)
					oledBuffer[xx + aaa] |= pixels >> (8 - pixelOffset);
					//setBuffByte(buff, xx, hhhh, pixels >> (8 - pixelOffset), colour);			
			}
		}		
	}
}

// y must be a multiple of 8
// height is always 8
void draw_clearArea(byte x, byte y, byte w)
{
	uint pos = x + (y / 8) * FRAME_WIDTH;
	memset(&oledBuffer[pos], 0x00, w);
}

void draw_end()
{
	oled_flush();
}

#define FONT_8X16_BASE_H		0x0F
#define FONT_8X16_BASE_L		0xCF00

#define FONT_16X32_BASE_H		0x0F
#define FONT_16X32_BASE_L		0xD4F0

#define FONT_HZ_BASE0			0x0C0000
#define FONT_HZ_BASE1			(FONT_HZ_BASE0 + 0x69C0)

void draw_8x16_char(char ch, byte x, byte y) 
{
	uint16_t offset;
	uint8_t *pointer;


	offset = FONT_8X16_BASE_L + ((uint16_t)(ch - ' ') << 4);
	pointer = oledBuffer + x + (y/8)*128;

	spi_read_data(FONT_8X16_BASE_H, offset, pointer, 8);
	spi_read_data(FONT_8X16_BASE_H, offset+8, pointer+128, 8);
}

void draw_16x32_char(char ch, byte x, byte y) 
{
	uint16_t offset;
	uint8_t *pointer;

	offset = FONT_16X32_BASE_L + ((uint16_t)(ch - ' ') * 64);
	pointer = oledBuffer + x + (y/8)*128;

	for(byte i=0; i<4; i++) {
		spi_read_data(FONT_16X32_BASE_H, offset, pointer, 16);
		pointer += 128;
		offset+=16;
	}
}

void draw_hz(uint16_t hz, byte x, byte y)
{
	uint32_t fontaddr;
	uint8_t *pointer;
	uint32_t H = (hz>>8)&0xff;
	uint32_t L = hz&0xff;
	uint16_t offset_l;
	uint8_t offset_H;

	if(H < 0xB0) {
		fontaddr = ((H - 0xA1)*94 + (L-0xA1))*32 + FONT_HZ_BASE0;
	} else {
		fontaddr = ((H - 0xB0)*94 + (L-0xA1))*32 + FONT_HZ_BASE1;
	}

	offset_l = fontaddr & 0xffff;
	offset_H = (fontaddr >> 16) & 0xff;
	pointer = oledBuffer + x + (y/8)*128;

	spi_read_data(offset_H, offset_l, pointer, 16);
	spi_read_data(offset_H, offset_l+16, pointer+128, 16);
}

